using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using SeataNet.Core.TCC;
using System.Collections.Concurrent;

namespace SeataNet.Samples.AccountService.Controllers
{
    /// <summary>
    /// 账户服务 - TCC 模式示例
    /// 演示与 Java Seata TCC 模式的兼容性
    /// </summary>
    [ApiController]
    [Route("api/[controller]")]
    public class AccountController : ControllerBase
    {
        private readonly ILogger<AccountController> _logger;
        private readonly IAccountTccService _accountTccService;

        public AccountController(
            ILogger<AccountController> logger,
            IAccountTccService accountTccService)
        {
            _logger = logger;
            _accountTccService = accountTccService;
        }

        /// <summary>
        /// 扣款接口 - 使用 TCC 模式
        /// </summary>
        [HttpPost("deduct")]
        public async Task<IActionResult> DeductBalance([FromBody] DeductBalanceRequest request)
        {
            _logger.LogInformation("开始扣款，用户ID：{UserId}，金额：{Amount}",
                request.UserId, request.Amount);

            try
            {
                // TCC Try 阶段
                var success = await _accountTccService.PrepareDeduct(
                    request.UserId,
                    request.Amount,
                    request.BusinessKey);

                if (success)
                {
                    _logger.LogInformation("扣款成功，用户ID：{UserId}", request.UserId);
                    return Ok(new { Success = true });
                }
                else
                {
                    _logger.LogWarning("扣款失败，用户ID：{UserId}", request.UserId);
                    return BadRequest(new { Success = false, Message = "余额不足" });
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "扣款异常，用户ID：{UserId}", request.UserId);
                throw;
            }
        }
    }

    /// <summary>
    /// 账户 TCC 服务接口
    /// 对应 Java: @LocalTCC
    /// </summary>
    [TccService]
    public interface IAccountTccService
    {
        /// <summary>
        /// Try 阶段：冻结资金
        /// 对应 Java: @TwoPhaseBusinessAction(name = "prepareDeduct")
        /// </summary>
        [TccAction(
            CommitMethod = nameof(Commit),
            RollbackMethod = nameof(Rollback)
        )]
        Task<bool> PrepareDeduct(string userId, decimal amount, string businessKey);

        /// <summary>
        /// Confirm 阶段：确认扣款
        /// 对应 Java: commit method
        /// </summary>
        Task<bool> Commit(TccContext context);

        /// <summary>
        /// Cancel 阶段：取消扣款（解冻）
        /// 对应 Java: rollback method
        /// </summary>
        Task<bool> Rollback(TccContext context);
    }

    /// <summary>
    /// 账户 TCC 服务实现
    /// </summary>
    public class AccountTccService : IAccountTccService
    {
        private readonly ILogger<AccountTccService> _logger;

        // 模拟数据库：账户余额
        private static readonly ConcurrentDictionary<string, decimal> _accounts = new();

        // 模拟数据库：冻结资金
        private static readonly ConcurrentDictionary<string, decimal> _frozenAmounts = new();

        public AccountTccService(ILogger<AccountTccService> logger)
        {
            _logger = logger;

            // 初始化测试数据
            _accounts.TryAdd("user001", 1000m);
            _accounts.TryAdd("user002", 2000m);
        }

        /// <summary>
        /// Try 阶段：冻结资金
        /// 业务逻辑：检查余额并冻结指定金额
        /// </summary>
        public async Task<bool> PrepareDeduct(string userId, decimal amount, string businessKey)
        {
            _logger.LogInformation("TCC Try: 冻结资金 - UserId={UserId}, Amount={Amount}, BusinessKey={BusinessKey}",
                userId, amount, businessKey);

            await Task.CompletedTask; // 模拟异步操作

            // 检查账户是否存在
            if (!_accounts.TryGetValue(userId, out var balance))
            {
                _logger.LogWarning("TCC Try 失败: 账户不存在 - UserId={UserId}", userId);
                return false;
            }

            // 检查余额是否充足
            var frozenAmount = _frozenAmounts.GetValueOrDefault(userId, 0m);
            var availableBalance = balance - frozenAmount;

            if (availableBalance < amount)
            {
                _logger.LogWarning("TCC Try 失败: 余额不足 - UserId={UserId}, Available={Available}, Required={Required}",
                    userId, availableBalance, amount);
                return false;
            }

            // 冻结资金
            _frozenAmounts.AddOrUpdate(userId, amount, (key, oldValue) => oldValue + amount);

            // 记录到 TCC 上下文（用于 Confirm/Cancel 阶段）
            var context = TccContext.Current;
            context.Data["userId"] = userId;
            context.Data["amount"] = amount;
            context.Data["businessKey"] = businessKey;
            context.Data["frozenTime"] = DateTime.UtcNow;

            _logger.LogInformation("TCC Try 成功: 已冻结资金 - UserId={UserId}, Amount={Amount}, FrozenTotal={FrozenTotal}",
                userId, amount, _frozenAmounts[userId]);

            return true;
        }

        /// <summary>
        /// Confirm 阶段：确认扣款
        /// 业务逻辑：扣减账户余额，释放冻结金额
        /// </summary>
        public async Task<bool> Commit(TccContext context)
        {
            // 从上下文获取参数
            var userId = context.Data["userId"]?.ToString();
            var amount = Convert.ToDecimal(context.Data["amount"]);
            var businessKey = context.Data["businessKey"]?.ToString();

            _logger.LogInformation("TCC Commit: 确认扣款 - UserId={UserId}, Amount={Amount}, BusinessKey={BusinessKey}",
                userId, amount, businessKey);

            await Task.CompletedTask; // 模拟异步操作

            // 幂等性检查：避免重复提交
            var commitKey = $"commit_{context.Xid}_{context.BranchId}";
            // 实际应该使用分布式锁或数据库唯一约束

            try
            {
                // 扣减账户余额
                if (_accounts.TryGetValue(userId!, out var balance))
                {
                    _accounts[userId!] = balance - amount;
                }

                // 释放冻结金额
                if (_frozenAmounts.TryGetValue(userId!, out var frozenAmount))
                {
                    _frozenAmounts[userId!] = frozenAmount - amount;
                }

                _logger.LogInformation("TCC Commit 成功: 已扣款 - UserId={UserId}, Amount={Amount}, NewBalance={NewBalance}",
                    userId, amount, _accounts[userId!]);

                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "TCC Commit 失败 - UserId={UserId}", userId);
                return false;
            }
        }

        /// <summary>
        /// Cancel 阶段：取消扣款
        /// 业务逻辑：解冻资金，恢复原状
        /// </summary>
        public async Task<bool> Rollback(TccContext context)
        {
            // 从上下文获取参数
            var userId = context.Data["userId"]?.ToString();
            var amount = Convert.ToDecimal(context.Data["amount"]);
            var businessKey = context.Data["businessKey"]?.ToString();

            _logger.LogInformation("TCC Cancel: 取消扣款（解冻） - UserId={UserId}, Amount={Amount}, BusinessKey={BusinessKey}",
                userId, amount, businessKey);

            await Task.CompletedTask; // 模拟异步操作

            // 幂等性检查：避免重复回滚
            var rollbackKey = $"rollback_{context.Xid}_{context.BranchId}";
            // 实际应该使用分布式锁或数据库唯一约束

            try
            {
                // 释放冻结金额
                if (_frozenAmounts.TryGetValue(userId!, out var frozenAmount))
                {
                    var newFrozenAmount = frozenAmount - amount;
                    if (newFrozenAmount <= 0)
                    {
                        _frozenAmounts.TryRemove(userId!, out _);
                    }
                    else
                    {
                        _frozenAmounts[userId!] = newFrozenAmount;
                    }
                }

                _logger.LogInformation("TCC Cancel 成功: 已解冻 - UserId={UserId}, Amount={Amount}",
                    userId, amount);

                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "TCC Cancel 失败 - UserId={UserId}", userId);
                return false;
            }
        }

        /// <summary>
        /// 查询账户余额（非事务方法）
        /// </summary>
        public Task<decimal> GetBalance(string userId)
        {
            _accounts.TryGetValue(userId, out var balance);
            return Task.FromResult(balance);
        }

        /// <summary>
        /// 查询冻结金额（非事务方法）
        /// </summary>
        public Task<decimal> GetFrozenAmount(string userId)
        {
            _frozenAmounts.TryGetValue(userId, out var frozen);
            return Task.FromResult(frozen);
        }
    }

    /// <summary>
    /// 扣款请求
    /// </summary>
    public class DeductBalanceRequest
    {
        public string UserId { get; set; } = string.Empty;
        public decimal Amount { get; set; }
        public string BusinessKey { get; set; } = string.Empty;
    }
}
